package ru.volgogradetzzz.threeD.camera {
	import flash.display.Stage;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import ru.volgogradetzz.math.VMatrix4x4;
	import ru.volgogradetzzz.math.VVector3;
	
	/**
	 * ...
	 * @author volgogradetzzz
	 */
	public class VCamera {
		
		private const _PREV_MOUSE_POS:VVector3 = new VVector3();
		private const _CURR_MOUSE_POS:VVector3 = new VVector3();
		
		private const _CAMERA_POSITION:VVector3 = new VVector3();
		private const _MODEL_ROTATION_CURR:VMatrix4x4 = new VMatrix4x4();
		private const _CAMERA_MOVE:VVector3 = new VVector3();
		private const _SPEED:Number = 0.01;
		private const _ACCELERATION:Number = 10;
		private const _AXIS:VVector3 = new VVector3();
		private const _DIFF:VVector3 = new VVector3();
		
		private var _stage:Stage;
		private var _mousePressed:Boolean = false;
		private var _upPressed:Boolean = false;
		private var _downPressed:Boolean = false;
		private var _leftPressed:Boolean = false;
		private var _rightPressed:Boolean = false;
		private var _accelerated:Boolean = false;
		
		private var _rotationSpeed:Number = 0;
		
		public function VCamera(stage:Stage, startPostion:VVector3) {
			_stage = stage;
			
			stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
			stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
			stage.addEventListener(MouseEvent.RIGHT_MOUSE_DOWN, rightMouseDown);
			stage.addEventListener(MouseEvent.RIGHT_MOUSE_UP, rightMouseUp);
		}
		
		public function update(cameraRotation:VMatrix4x4, cameraTransform:VMatrix4x4):void {
			_CAMERA_MOVE.zero();
			
			if (_upPressed) {
				_CAMERA_MOVE.z = _SPEED;
			}
			
			if (_downPressed) {
				_CAMERA_MOVE.z = -_SPEED;
			}
			
			if (_leftPressed) {
				_CAMERA_MOVE.x = -_SPEED;
			}
			
			if (_rightPressed) {
				_CAMERA_MOVE.x = _SPEED;
			}
			
			if (_accelerated) {
				_CAMERA_MOVE.scale(_ACCELERATION);
			}
			
			if (_mousePressed) {
				_rotationSpeed = 0;
				
				_CURR_MOUSE_POS.x = _stage.mouseX;
				_CURR_MOUSE_POS.y = _stage.mouseY;
				
				_CURR_MOUSE_POS.subtractCopyTo(_PREV_MOUSE_POS, _DIFF);
				
				_rotationSpeed = _DIFF.squareMagnitude() * 0.001;
				if (_rotationSpeed > 0.1) {
					_rotationSpeed = 0.1;
				}
				
				_AXIS.x = _DIFF.y;
				_AXIS.y = _DIFF.x;
				_AXIS.multiplyByMatrix(cameraRotation);
				_AXIS.normalize();
				
				_PREV_MOUSE_POS.x = _stage.mouseX;
				_PREV_MOUSE_POS.y = _stage.mouseY;
			}else {
				_rotationSpeed *= 0.8;
			}
			
			if (_rotationSpeed > 0.0001) {
				var c:Number = Math.cos(_rotationSpeed);
				var s:Number = Math.sin(_rotationSpeed);
				var t:Number = 1 - c;
				
				_MODEL_ROTATION_CURR.m11 = t * _AXIS.x * _AXIS.x + c;
				_MODEL_ROTATION_CURR.m12 = t * _AXIS.x * _AXIS.y + s * _AXIS.z;
				_MODEL_ROTATION_CURR.m13 = t * _AXIS.x * _AXIS.z - s * _AXIS.y;
				
				_MODEL_ROTATION_CURR.m21 = t * _AXIS.x * _AXIS.y - s * _AXIS.z;
				_MODEL_ROTATION_CURR.m22 = t * _AXIS.y * _AXIS.y + c;
				_MODEL_ROTATION_CURR.m23 = t * _AXIS.y * _AXIS.z + s * _AXIS.x;
				
				_MODEL_ROTATION_CURR.m31 = t * _AXIS.x * _AXIS.z + s * _AXIS.y;
				_MODEL_ROTATION_CURR.m32 = t * _AXIS.y * _AXIS.z - s * _AXIS.x;
				_MODEL_ROTATION_CURR.m33 = t * _AXIS.z * _AXIS.z + c;
				
				cameraRotation.multiplyByMatrix(_MODEL_ROTATION_CURR);
			}
			
			_CAMERA_MOVE.multiplyByMatrix(cameraRotation);
			_CAMERA_POSITION.add(_CAMERA_MOVE);
			
			cameraTransform.copyFrom(cameraRotation);
			cameraTransform.setupTranslation(_CAMERA_POSITION.x, _CAMERA_POSITION.y, _CAMERA_POSITION.z);
		}
		
		private function keyDownHandler(event:KeyboardEvent):void {
			switch(event.keyCode) {
				//w, up
				case 87:
				case 38:
					_upPressed = true;
					break;
				//s, down
				case 83:
				case 40:
					_downPressed = true;
					break;
				//a, left
				case 65:
				case 37:
					_leftPressed = true;
					break;
				//d, right
				case 68:
				case 39:
					_rightPressed = true;
					break;
				//shift
				case 16:
					_accelerated = true;
					break;
			}
		}
		
		private function keyUpHandler(event:KeyboardEvent):void {
			switch(event.keyCode) {
				//w, up
				case 87:
				case 38:
					_upPressed = false;
					break;
				//s, down
				case 83:
				case 40:
					_downPressed = false;
					break;
				//a, left
				case 65:
				case 37:
					_leftPressed = false;
					break;
				//d, right
				case 68:
				case 39:
					_rightPressed = false;
					break;
				//shift
				case 16:
					_accelerated = false;
					break;
			}
		}
		
		private function rightMouseDown(event:MouseEvent):void {
			_mousePressed = true;
			
			_PREV_MOUSE_POS.x = _stage.mouseX;
			_PREV_MOUSE_POS.y = _stage.mouseY;
		}
		
		private function rightMouseUp(event:MouseEvent):void {
			_mousePressed = false;
		}
	}
}